#ifndef MESH_H
#define MESH_H
#include "ModelAnimation.h"
#include "Skeleton.h"

#include <glm/glm.hpp>
#include <cstdint>
#include <string>
#include <vector>
#include <list>
#include <Cache.h>
#include <memory>
#include <IShader.h>
#include <Texture.h>

#define MAX_BONE_LENGTH 4
#define MAX_BONE_ANIM_LENGTH 128

class OrderedMesh;
class MeshInstance;
class SkeletonAsset;
class Skeleton;

struct Vertex
{
    glm::vec3 Position = {0, 0, 0};
    glm::vec3 Normal = {0, 0, 0};
    glm::vec2 TexCoords = {0, 0};

    uint32_t RenderMask = 0xFFFFFFFF;
    int32_t BoneId[MAX_BONE_LENGTH] = {0};
    float BoneWeight[MAX_BONE_LENGTH] = {0};
};

struct InstanceData{
public:
    uint32_t DrawId;
    uint32_t DrawMask;

    InstanceData(uint32_t id, uint32_t mask) : DrawId(id), DrawMask(mask) {}
    InstanceData() : DrawId(0), DrawMask(0xFFFFFFFF) {}
};

class MeshAsset
{
public:
    std::string name;
    std::unordered_map<int, std::string> boneIdtoNames;
    std::vector<Vertex> vertices;
    std::vector<uint32_t> indices;
    Texture texture;
    MeshAsset(const char *name, const std::vector<Vertex> &vertices, const std::vector<uint32_t> &indices, const Texture &texture, std::unordered_map<int, std::string> boneIdtoNames);
    MeshAsset(const std::string& path);

    std::shared_ptr<OrderedMesh> OrderBindedMesh(std::shared_ptr<SkeletonAsset> skeletonAsset);
    std::shared_ptr<OrderedMesh> OrderMesh();

    void ExportAsset(const std::string& path);

};

// Mesh Asset 绑定到指定一个骨架上之后，更新自己的节点骨骼id，准备创建mesh渲染实例
class OrderedMesh
{
public:
    friend class MeshAsset;
    const int maxInstanceCount = 4096;
    bool hasBodyPart;
    Texture textureBody;
    Texture texture;

    OrderedMesh(const std::vector<Vertex> &vertices,const std::vector<uint32_t> &indices, const Texture &texture)
    {
        this->vertices = std::vector<Vertex>(vertices);
        this->indices = std::vector<uint32_t>(indices);
        this->texture = texture;
    }

    OrderedMesh(const char *filepath, std::shared_ptr<SkeletonAsset> skeletonAsset);

    std::shared_ptr<MeshInstance> CreateBindedInstance(std::shared_ptr<Skeleton> skeleton);
    std::shared_ptr<MeshInstance> CreateInstance();

    void DrawInstance(const gl::IShader &shader);
    void SetupMesh();
    void FlushInstance();
    void AddInstacnce(MeshInstance *instance, uint32_t instanceId, uint32_t instanceMask);

    void ExportToFile(const char *fileName);

    void SetTexture(const Texture &texture)
    {
        this->texture = texture;
    }

    Texture GetTexture()
    {
        return this->texture;
    }

private:
    std::vector<Vertex> vertices;
    std::vector<uint32_t> indices;
    uint32_t VAO, VBO, EBO, instanceVBO;
    bool initialized = false;

    bool skeletonBinded = false;
    std::shared_ptr<SkeletonAsset> skeletonAsset;

    uint32_t instanceCount = 0;
    std::vector<InstanceData> instanceDatas;
    // std::vector<uint32_t> meshDrawIds;
    // std::vector<uint32_t> meshDrawMask;

    std::list<MeshInstance*> instancesToDraw;
};

class MeshInstance
{
public:
    std::shared_ptr<OrderedMesh> mesh;
    std::shared_ptr<Skeleton> skeleton;
    glm::mat4 modelMatrix;
    int drawId = -1;
    uint32_t drawMask = 0xFFFFFFFF;
    MeshInstance(std::shared_ptr<OrderedMesh> mesh, glm::mat4 modelMatrix);
    void ToDraw();
};

#endif // MESH_H
